package com.redhat.ceylon.model.loader.model;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.redhat.ceylon.model.loader.mirror.AnnotationMirror;
import com.redhat.ceylon.model.typechecker.model.Class;
/**
* Mirrors the elements of {@link java.lang.annotation.ElementType} for
* the purpose of targeting java annotations.
*/
public enum AnnotationTarget {
TYPE {
@Override
public Set<OutputElement> outputs() {
return Collections.singleton(OutputElement.TYPE);
}
},
FIELD {
@Override
public Set<OutputElement> outputs() {
return Collections.singleton(OutputElement.FIELD);
}
},
METHOD {
@Override
public Set<OutputElement> outputs() {
HashSet<OutputElement> result = new HashSet<OutputElement>(3);
result.add(OutputElement.METHOD);
result.add(OutputElement.GETTER);
result.add(OutputElement.SETTER);
return result;
}
},
PARAMETER {
@Override
public Set<OutputElement> outputs() {
return Collections.singleton(OutputElement.PARAMETER);
}
},
CONSTRUCTOR {
@Override
public Set<OutputElement> outputs() {
return Collections.singleton(OutputElement.CONSTRUCTOR);
}
},
LOCAL_VARIABLE {
@Override
public Set<OutputElement> outputs() {
return Collections.singleton(OutputElement.LOCAL_VARIABLE);
}
},
ANNOTATION_TYPE {
@Override
public Set<OutputElement> outputs() {
return Collections.singleton(OutputElement.ANNOTATION_TYPE);
}
},
PACKAGE {
@Override
public Set<OutputElement> outputs() {
return Collections.singleton(OutputElement.PACKAGE);
}
},
TYPE_USE {
@Override
public Set<OutputElement> outputs() {
return Collections.emptySet();
}
},
TYPE_PARAMETER {
@Override
public Set<OutputElement> outputs() {
return Collections.emptySet();
}
};
public abstract Set<OutputElement> outputs();
/**
* Returns the elements in the {@code @Target} annotation of the given
* {@code @interface}, or null if
* the annotation type lacks the {@code @Target} annotation.
*/
public static EnumSet<AnnotationTarget> getAnnotationTarget(LazyInterface annotationType) {
AnnotationMirror targetAnno = annotationType.classMirror.getAnnotation("java.lang.annotation.Target");
if (targetAnno != null) {
@SuppressWarnings("unchecked")
List<String> targets = (List<String>)targetAnno.getValue();
EnumSet<AnnotationTarget> result = EnumSet.noneOf(AnnotationTarget.class);
for (String name : targets) {
result.add(AnnotationTarget.valueOf(name));
}
return result;
}
return null;
}
/**
* Returns the possible targets of the given annotation proxy class
* (according to the {@code @Target} of the {@code @interface} that
* the class is a proxy to),
* or null if {@code @interface} lacks a {@code @Target} or if
* the given class is not an annotation proxy.
*/
private static EnumSet<AnnotationTarget> annotationTargets(Class annotationClass) {
if (annotationClass instanceof AnnotationProxyClass) {
return getAnnotationTarget(((AnnotationProxyClass)annotationClass).iface);
} else {
return null;
}
}
/**
* Given a set of Java annotation constraints, returns the
* possible Java elements that could be generated by Ceylon elements
* that the annotation could be applied to.
* @param targets
* @return
*/
private static EnumSet<OutputElement> possibleCeylonTargets(EnumSet<AnnotationTarget> targets) {
if (targets == null) {
targets = EnumSet.allOf(AnnotationTarget.class);
}
EnumSet<OutputElement> result = EnumSet.noneOf(OutputElement.class);
for (AnnotationTarget t : targets) {
result.addAll(t.outputs());
}
return result;
}
/**
* The set of program elements the given
* annotation class could be applied to, according <strong>only</strong> to
* the {@code @Target}s on the corresponding {@code @interface}.
* @param annotationClass
* @return
*/
public static EnumSet<OutputElement> outputTargets(Class annotationClass) {
return possibleCeylonTargets(annotationTargets(annotationClass));
}
}